<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="李修倩在 Github 上的个人博客">
    <meta name="keyword" content="null">
    <meta name="theme-color" content="#600090">
    <meta name="msapplication-navbutton-color" content="#600090">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="#600090">
    <link rel="shortcut icon" href="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-person-128.png">
    <link rel="alternate" type="application/atom+xml" title="Xiuqianli&#39;s blog" href="/atom.xml">
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.css">
    <title>
        
        手写山寨spring基础功能 - MVC(1) - 路由注册和参数绑定｜xiuqianli&#39;s blog
        
    </title>

    <link rel="canonical" href="https://981764793.gitee.io/2018/07/17/手写山寨spring基础功能-MVC-1-路由注册和参数绑定/">

    <!-- Bootstrap Core CSS -->
    <link rel="stylesheet" href="/css/bootstrap.min.css">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/blog-style.css">

    <!-- Pygments Github CSS -->
    <link rel="stylesheet" href="/css/syntax.css">
</head>

<style>

    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }
</style>
<!-- hack iOS CSS :active style -->
<body ontouchstart="" class="animated fadeIn">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top " id="nav-top" data-ispost = "true" data-istags="false
" data-ishome = "false" >
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand animated pulse" href="/">
                <span class="brand-logo">
                    Xiuqianli&#39;s blog
                </span>
                's Blog
            </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <!-- /.navbar-collapse -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
					
                    
                        
							
                        <li>
                            <a href="/Tags/">Tags</a>
                        </li>
							
						
                    
					
					
                </ul>
            </div>
        </div>
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
//    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>

<!-- Main Content -->

<!--only post-->


<img class="wechat-title-img"
     src="http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80">


<style>
    
    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }

    
</style>

<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <div class="post-heading">
                    <h1>手写山寨spring基础功能 - MVC(1) - 路由注册和参数绑定</h1>
                    
                    <span class="meta">
                         作者 Xiuqian Li
                        <span>
                          日期 2018-07-17
                         </span>
                    </span>
                    <div class="tags text-center">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="post-title-haojen">
        <span>
            手写山寨spring基础功能 - MVC(1) - 路由注册和参数绑定
        </span>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- Post Container -->
            <div class="col-lg-8 col-lg-offset-1 col-sm-9 post-container">
                <h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>在之前已经实现了ioc和aop，接下来就到mvc模块了，前两篇文章可以看我的专栏。</p>
<h4 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h4><p>先做一个简单的、假的需求分析。就我目前的使用程度来说，springmvc中最常用到的功能不多，就只是路由、参数绑定、异常处理、视图处理而已，权限校验和参数校验理论上是可以配合之前实现的aop模块实现的，所以在LSFramework中就准备实现我用过的这四大功能模块。这一篇先记录一下我实现路由注册和参数绑定的过程。</p>
<h4 id="实现过程"><a href="#实现过程" class="headerlink" title="实现过程"></a>实现过程</h4><h5 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h5><p>spring中通过DispatchServlet实现路由分发，并且围绕着它实现了一堆强大的功能，所以我也是先实现了路由分发这一块。其中涉及到的注解有Controller、RequestMapping、GetMapping等，用于支持路径和HttpMethod绑定，我这里就偷懒不管HttpMethod了，也不管Controller了，直接用LSBean代表它，只写一个LSRequestMapping，然后是参数绑定用的</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@Target(&#123;ElementType.TYPE, ElementType.METHOD&#125;)</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Documented</span><br><span class="line">public @interface LSRequestMapping &#123;</span><br><span class="line">    String value();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>实现WebLoader扫描路由, Loader机制可以看ls-core模块下的ApplicationContext，后续有时间可能也会单独记一篇笔记。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@LSLoader(1000)</span><br><span class="line">public class WebLoader implements Loader &#123;</span><br><span class="line"></span><br><span class="line">    private static final Logger logger = Logger.getLogger(WebLoader.class);</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void doLoad(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line"></span><br><span class="line">        //装载视图处理器</span><br><span class="line">        ClassUtil.getClassesByInterface(classSet, ViewResolver.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; !Modifier.isAbstract(clazz.getModifiers()))</span><br><span class="line">                .map(ObjectKit::getInstance)</span><br><span class="line">                .filter(ObjectKit::notNull)</span><br><span class="line">                .map(o -&gt; (ViewResolver)o)</span><br><span class="line">                .forEach(ViewResolversContainer::add);</span><br><span class="line"></span><br><span class="line">        //装载参数处理器</span><br><span class="line">        ClassUtil.getClassesByInterface(classSet, ParameterResolver.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; !Modifier.isAbstract(clazz.getModifiers()))</span><br><span class="line">                .map(ObjectKit::getInstance)</span><br><span class="line">                .filter(ObjectKit::notNull)</span><br><span class="line">                .map(o -&gt; (ParameterResolver)o)</span><br><span class="line">                .forEach(ParameterResolverContainer::add);</span><br><span class="line"></span><br><span class="line">        //装载异常处理器</span><br><span class="line">        ClassUtil.getClassesByInterface(classSet, ExceptionResolver.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; !Modifier.isAbstract(clazz.getModifiers()))</span><br><span class="line">                .map(ObjectKit::getInstance)</span><br><span class="line">                .filter(ObjectKit::notNull)</span><br><span class="line">                .map(o -&gt; (ExceptionResolver)o)</span><br><span class="line">                .forEach(ExceptionResolverContainer::add);</span><br><span class="line"></span><br><span class="line">        //扫描被LSBean和LSRequestMapping修饰的类，装载路由</span><br><span class="line">        ClassUtil.getClassesByAnnotation(classSet, LSRequestMapping.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .filter(clazz -&gt; clazz.isAnnotationPresent(LSBean.class))</span><br><span class="line">                .forEach(HandlerHelper::initRoute);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>用到java8的stream api，还有ioc那一篇写的工具类，工具类代码都不复杂，不多解释，WebLoader就是遍历扫描出来的所有class，筛选出符合条件的添加到不同的组件容器内，这一篇笔记先记录装载路由，其他仨过两天再说。</p>
<p>路由的思路是过滤出有LSRequestMapping注解和LSBean注解的类，调用HandlerHelper的initRoute方法（所以只要你愿意，随时可以花两分钟把Service类变成restful api）。</p>
<p>所以接下来看HandlerHelper类<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class HandlerHelper &#123;</span><br><span class="line"></span><br><span class="line">    private static final String pathParamPlaceholderRegx = &quot;\\&#123;(\\w+)\\&#125;&quot;; //LSRequestMapping注解的path参数名正则</span><br><span class="line">    private static final String pathParamRegx = &quot;([0-9a-zA-Z\\\\-_]+)&quot;;//真正的参数正则，把占位符换成这玩意</span><br><span class="line"></span><br><span class="line">//    private static final Logger logger = Logger.getLogger(HandlerHelper.class);</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 装载路由</span><br><span class="line">     * @param clazz</span><br><span class="line">     */</span><br><span class="line">    public static void initRoute(Class&lt;?&gt; clazz) &#123;</span><br><span class="line">//        System.out.println(clazz.getName());</span><br><span class="line">        LSRequestMapping classRequestMapping = clazz.getAnnotation(LSRequestMapping.class);</span><br><span class="line">        for (Method method : clazz.getMethods())&#123;</span><br><span class="line">            if (!method.isAnnotationPresent(LSRequestMapping.class)) &#123;</span><br><span class="line">                continue;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            LSRequestMapping methodRequestMapping = method.getAnnotation(LSRequestMapping.class);</span><br><span class="line">            String mappingUrl = &quot;/&quot; + classRequestMapping.value() + &quot;/&quot; + methodRequestMapping.value();</span><br><span class="line">            mappingUrl = mappingUrl.replaceAll(&quot;/+&quot;, &quot;/&quot;);</span><br><span class="line">            buildMapping(clazz, method, mappingUrl);</span><br><span class="line"></span><br><span class="line">//            logger.info(&quot;mapping \&quot;&quot; + mappingUrl + &quot;\&quot; to &quot; + clazz.getName() + &quot;.&quot; + method.getName());</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private static void buildMapping(Class&lt;?&gt; controllerClass, Method actionMethod, String mappingUrl) &#123;</span><br><span class="line">        String regxUrl = mappingUrl.replaceAll(pathParamPlaceholderRegx, pathParamRegx);</span><br><span class="line">//        Pattern pattern = Pattern.compile(regxUrl);</span><br><span class="line"></span><br><span class="line">        ActionHandler actionHandler = new ActionHandler();</span><br><span class="line">        actionHandler.setActionMethod(actionMethod);</span><br><span class="line">//        actionHandler.setActionPattern(pattern);</span><br><span class="line">        actionHandler.setControllerClass(controllerClass);</span><br><span class="line">        actionHandler.setControllerInstance(BeanContainer.getBean(controllerClass));</span><br><span class="line">        actionHandler.setMappingUrl(mappingUrl);</span><br><span class="line">        actionHandler.setRegxUrl(regxUrl);</span><br><span class="line"></span><br><span class="line">        Pattern pathParamNamePattern = Pattern.compile(pathParamPlaceholderRegx);</span><br><span class="line">        Matcher matcher = pathParamNamePattern.matcher(mappingUrl);</span><br><span class="line">        List&lt;String&gt; pathParamNames = new ArrayList&lt;&gt;();</span><br><span class="line">        while (matcher.find()) &#123;</span><br><span class="line">            pathParamNames.add(matcher.group(1));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        actionHandler.setPathParamNames(pathParamNames);</span><br><span class="line"></span><br><span class="line">        HandlerContainer.add(actionHandler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>initRoute只干了一件事，遍历参数class里的所有public修饰且有LSRequestMapping的方法，把类上注解的值和方法上注解的值通过斜杠连接起来，然后再把多个斜杠替换成一个，接下来就交给buildMapping来构造真正的路由对象。</p>
<p>buildMapping里把拼接完的mappingUrl替换掉pathparam占位符变成一个正则表达式，并且通过mappingUrl用正则获取所有path参数的名称，用controller实例（传入的class）、目标方法等参数构建一个新的ActionHandler对象，这个就是真正的路由对象了，它用来处理匹配上的http请求，其中HandlerContainer就只是一个静态的List。</p>
<p>接下来看ActionHandler<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class ActionHandler &#123;</span><br><span class="line"></span><br><span class="line">    private static Logger logger = Logger.getLogger(ActionHandler.class);</span><br><span class="line"></span><br><span class="line">    private Object controllerInstance;</span><br><span class="line">    private Class controllerClass;</span><br><span class="line">    private Method actionMethod;</span><br><span class="line">    private Pattern actionPattern;</span><br><span class="line">    private String mappingUrl;</span><br><span class="line">    private String regxUrl;</span><br><span class="line">    private List&lt;String&gt; pathParamNames;</span><br><span class="line"></span><br><span class="line">    public Object invoke(String url, HttpServletRequest request, HttpServletResponse response) throws Exception &#123;</span><br><span class="line">        logger.info(request.getMethod() + &quot; === &quot; + request.getRequestURL());</span><br><span class="line">        Map&lt;String, String&gt; pathParamMap = PathParamKit.buildPathParam(url, actionPattern, pathParamNames);</span><br><span class="line">        Object[] args = new Object[actionMethod.getParameterCount()];</span><br><span class="line">        int pos = 0;</span><br><span class="line">        for (Parameter parameter : actionMethod.getParameters()) &#123;</span><br><span class="line">            args[pos++] = ParameterResolverContainer.handle(actionMethod, parameter, request, response, pathParamMap);</span><br><span class="line">        &#125;</span><br><span class="line">        try &#123;</span><br><span class="line">            return actionMethod.invoke(controllerInstance, args);</span><br><span class="line">        &#125; catch (IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; catch (InvocationTargetException e) &#123;</span><br><span class="line">            throw (Exception) e.getTargetException();</span><br><span class="line">        &#125;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public boolean match(String url) &#123;</span><br><span class="line">        return actionPattern.matcher(url).matches();</span><br><span class="line">    &#125;</span><br><span class="line">    //getter、setter</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>先说简单的，match就是通过正则判断当前ActionHandler是否跟目标url匹配。<br>invoke就是执行真正的Method，需要的参数有url、HttpServletRequest, HttpServletResponse，执行顺序：先调用PathParamKit.buildPathParam构造PathParameter(路径参数，实在不知道怎么个描述)，然后遍历目标方法的所有参数，通过ParameterResolverContainer遍历ParameterResolver构造出参数数组，最后直接invoke执行。</p>
<h5 id="开始实现参数绑定"><a href="#开始实现参数绑定" class="headerlink" title="开始实现参数绑定"></a>开始实现参数绑定</h5><p>写的注解：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@Target(ElementType.PARAMETER)</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Documented</span><br><span class="line">public @interface LSPathParam &#123;</span><br><span class="line">    String value();//参数名</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * defaultValue必须在require为false的情况下生效</span><br><span class="line"> */</span><br><span class="line">@Target(ElementType.PARAMETER)</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Documented</span><br><span class="line">public @interface LSRequestParam &#123;</span><br><span class="line">    String value();</span><br><span class="line">    boolean require() default true;</span><br><span class="line">    String defaultValue() default &quot;&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>PathParamKit<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class PathParamKit &#123;</span><br><span class="line"></span><br><span class="line">    public static Map&lt;String, String&gt; buildPathParam(String url, Pattern pattern, List&lt;String&gt; paramNames) &#123;</span><br><span class="line">        Matcher matcher = pattern.matcher(url);</span><br><span class="line">        Map&lt;String, String&gt; resultMap = new HashMap&lt;&gt;();</span><br><span class="line">        if (matcher.find()) &#123;</span><br><span class="line">            for (int i = 0; i &lt; paramNames.size(); i++) &#123;</span><br><span class="line">                String key = paramNames.get(i);</span><br><span class="line">                String value = matcher.group(i + 1);</span><br><span class="line">                resultMap.put(key, value);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return resultMap;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>把ActionHandler的actionPattern（buildMapping的时候替换的结果）、pathParamNames（buildMapping的时候用正则取出来的）和当前的url传进来，通过正则匹配的结果和名字填充map。</p>
<p>接下来是是构造参数列表，用到了ParameterResolverContainer.handle</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class ParameterResolverContainer &#123;</span><br><span class="line"></span><br><span class="line">    private static List&lt;ParameterResolver&gt; parameterResolverList = new LinkedList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    public static void add(ParameterResolver parameterResolver) &#123;</span><br><span class="line">        parameterResolverList.add(parameterResolver);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Object handle(Method actionMethod, Parameter parameter, HttpServletRequest request, HttpServletResponse response, Map&lt;String, String&gt; pathParamMap) &#123;</span><br><span class="line"></span><br><span class="line">        for (ParameterResolver parameterResolver : parameterResolverList) &#123;</span><br><span class="line">            if (parameterResolver.filter(actionMethod, parameter, request, response, pathParamMap)) &#123;</span><br><span class="line">                return parameterResolver.handle(actionMethod, parameter, request, response, pathParamMap);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>ParameterResolverContainer本质也是一个List，handle就是遍历而已，重点是ParameterResolver，ParameterResolver是一个接口，实现这个接口后添加到ParameterResolverContainer即可实现自定义参数处理。接下来看两个实现类，ServletRequestParamResolver实现绑定HttpServletRequest, RequestParamResolver实现绑定request参数</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class ServletRequestParamResolver implements ParameterResolver &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public boolean filter(Method actionMethod, Parameter parameter, HttpServletRequest request, HttpServletResponse response, Map&lt;String, String&gt; pathParamMap) &#123;</span><br><span class="line">        return parameter.getType() == HttpServletRequest.class; //判断是否由这个类处理这个参数</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public Object handle(Method actionMethod, Parameter parameter, HttpServletRequest request, HttpServletResponse response, Map&lt;String, String&gt; pathParamMap) &#123;</span><br><span class="line">        return request;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">public class RequestParamResolver implements ParameterResolver &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public boolean filter(Method actionMethod, Parameter parameter, HttpServletRequest request, HttpServletResponse response, Map&lt;String, String&gt; pathParamMap) &#123;</span><br><span class="line">        return parameter.isAnnotationPresent(LSRequestParam.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public Object handle(Method actionMethod, Parameter parameter, HttpServletRequest request, HttpServletResponse response, Map&lt;String, String&gt; pathParamMap) &#123;</span><br><span class="line">        LSRequestParam lsRequestParam = parameter.getAnnotation(LSRequestParam.class);</span><br><span class="line">        String paramName = lsRequestParam.value();</span><br><span class="line">        String param = request.getParameter(paramName);</span><br><span class="line"></span><br><span class="line">        //判断是否为空 判断是抛出异常还是取默认值</span><br><span class="line">        if (StringKit.isBlank(param)) &#123;</span><br><span class="line">            if (lsRequestParam.require()) &#123;</span><br><span class="line">                throw new MissingRequestParamException(ClassUtil.getFullMethodName(actionMethod), paramName);</span><br><span class="line">            &#125;</span><br><span class="line">            // 取默认参数</span><br><span class="line">            param = lsRequestParam.defaultValue();</span><br><span class="line">        &#125;</span><br><span class="line">        return ConvertUtil.convert(param, parameter.getType());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过filter判断是否处理，handle进行真正的处理，就是把返回值作为参数值对象。</p>
<h4 id="实现路由分发"><a href="#实现路由分发" class="headerlink" title="实现路由分发"></a>实现路由分发</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class LSDispatchServlet extends HttpServlet &#123;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException &#123;</span><br><span class="line">        req.setCharacterEncoding(&quot;UTF-8&quot;);</span><br><span class="line">        resp.setCharacterEncoding(&quot;UTF-8&quot;);</span><br><span class="line">        RequestContext.setRequest(req);</span><br><span class="line">        RequestContext.setResponse(resp);</span><br><span class="line">        String formatUrl = req.getRequestURI().replaceAll(&quot;/+&quot;, &quot;/&quot;);//替换掉多余斜杠，其实可以考虑去掉这一句</span><br><span class="line">        ActionHandler actionHandler = HandlerContainer.get(formatUrl);//遍历HandlerContainer取ActionHandler</span><br><span class="line">//        System.out.println(req.getRequestURI());</span><br><span class="line">        if (actionHandler != null) &#123;</span><br><span class="line">            Object result = null;</span><br><span class="line">            Exception exception = null;</span><br><span class="line">            try &#123;</span><br><span class="line">                result = actionHandler.invoke(formatUrl, req, resp);</span><br><span class="line">            &#125; catch (Exception e) &#123;</span><br><span class="line">                exception = e;</span><br><span class="line">            &#125;</span><br><span class="line">            try &#123;</span><br><span class="line">                processDispatchResult(actionHandler, result, req, resp, exception);</span><br><span class="line">            &#125; catch (Exception e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                throw new ServletException(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            resp.sendError(404);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        resp.flushBuffer();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void processDispatchResult(ActionHandler actionHandler, Object result, HttpServletRequest request</span><br><span class="line">            , HttpServletResponse response, Exception exception) throws Exception &#123;</span><br><span class="line">        if (exception != null) &#123;</span><br><span class="line">            result = ExceptionResolverContainer.handle(actionHandler, exception, request, response);</span><br><span class="line">            if (result == null) &#123;</span><br><span class="line">                throw exception;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        if (result != null) &#123;</span><br><span class="line">            ViewResolversContainer.handle(actionHandler, result, request, response);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>一句话，遍历所有的ActionHandler，根据url取到对应的之后调用它的invoke方法，然后进行结果处理，如果遍历完了都没有就抛出404。</p>

                <hr>
                

                <ul class="pager">
                    
                    <li class="previous">
                        <a href="/2018/07/17/手写山寨spring基础功能-MVC-2-异常处理和视图处理/" data-toggle="tooltip" data-placement="top"
                           title="手写山寨spring基础功能 - MVC(2) - 异常处理和视图处理">&larr; Previous Post</a>
                    </li>
                    
                    
                    <li class="next">
                        <a href="/2018/07/17/手写山寨spring基础功能-AOP/" data-toggle="tooltip" data-placement="top"
                           title="手写山寨spring基础功能 - AOP">Next Post &rarr;</a>
                    </li>
                    
                </ul>

                

                


                <!--加入新的评论系统-->
                
            </div>

            <div class="hidden-xs col-sm-3 toc-col">
                <div class="toc-wrap">
                    <ol class="toc"><li class="toc-item toc-level-4"><a class="toc-link" href="#前言"><span class="toc-text">前言</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#需求分析"><span class="toc-text">需求分析</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#实现过程"><span class="toc-text">实现过程</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#路由"><span class="toc-text">路由</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#开始实现参数绑定"><span class="toc-text">开始实现参数绑定</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#实现路由分发"><span class="toc-text">实现路由分发</span></a></li></ol>
                </div>
            </div>
        </div>

        <div class="row">
            <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5 class="text-center">
                        <a href="/tags/">FEATURED TAGS</a>
                    </h5>
                    <div class="tags">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
            </div>
        </div>

    </div>
</article>







<!-- Footer -->
<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <br>
                <ul class="list-inline text-center">
                
                
                
                    <li>
                        <a target="_blank" href="https://www.zhihu.com/people/asdf1234-97">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa  fa-stack-1x fa-inverse">知</i>
                            </span>
                        </a>
                    </li>
                

                

                

                
                    <li>
                        <a target="_blank"  href="https://github.com/xiuqianli1996">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; Xiuqianli&#39;s blog 2018
                    <br>
                    <span id="busuanzi_container_site_pv" style="font-size: 12px;">PV: <span id="busuanzi_value_site_pv"></span> Times</span>
                    <br>
                    Theme by <a href="https://haojen.github.io/">Haojen Ma</a>
                </p>

            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->
<script src="/js/jquery.min.js"></script>

<!-- Bootstrap Core JavaScript -->
<script src="/js/bootstrap.min.js"></script>

<!-- Custom Theme JavaScript -->
<script src="/js/blog.js"></script>

<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>

<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if($('#tag_cloud').length !== 0){
        async("https://981764793.gitee.io/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>

<!-- Google Analytics -->



<!-- Baidu Tongji -->


<!-- swiftype -->
<script type="text/javascript">
  (function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
  (w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
  e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
  })(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

  _st('install','null','2.0.0');
</script>

<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<!--wechat title img-->
<img class="wechat-title-img" src="null">
</body>

</html>
